// <copyright file="TileData.cs" company="TenneySoftware">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Robert Tenney</author>
// <email>rob10e@yahoo.com</email>
// <date>April 2009</date>
// <summary>TileData contains the data needed for each tile.</summary>

// License:

// Product: RPGRoguelikeEngine .Net
// Author: Robert N. Tenney
// Copyright: April 2009

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation version 2
// of the License.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// This license covers all source code, binaries, and other materials related to this project
// unless specifically stated otherwise.

using System;
using System.Collections.Generic;
using System.Drawing;

using TenneySoftware.Engine.Characters;

namespace TenneySoftware.Engine {
    /// <summary>
    /// TileData contains the data needed for each tile.
    /// </summary>
    public class TileData {
        /// <summary>
        /// Type that this tile is.
        /// </summary>
        private TileType tileType = TileType.Wall;
        
        // TODO: Add Door data here
        // TODO: Add Object List here
        // TODO: Add Creature data here
        // FIXME: Check to see if the different TileStateChange and TileChange are both needed.
        
        /// <summary>
        /// Character, if here.
        /// </summary>
        private CharacterBase character;
        
        /// <summary>
        /// Has this tile been previously seen.
        /// </summary>
        private bool hasSeen;
        
        /// <summary>
        /// Can this tile be seen.
        /// </summary>
        private bool canSee;
        
        /// <summary>
        /// Is this tile transparent (does not block the line of sight).
        /// </summary>
        private bool transparent;
        
        /// <summary>
        /// Has this tile changed since last draw update.
        /// </summary>
        private bool tileChanged = true;
        
        /// <summary>
        /// Was this tile seen before the canSee value changed.
        /// </summary>
        private bool seenLast;
        
        /// <summary>
        /// Has the state of the tile changed, is an update needed.
        /// </summary>
        private bool tileStateChanged = true;
        
        /// <summary>
        /// Type variation for this tile.
        /// </summary>
        private TileTypeVariation typeVariation;
        
        /// <summary>
        /// Description of this tile.
        /// </summary>
        private string tileDescription;

        /// <summary>
        /// Initializes a new instance of the TileData class.
        /// </summary>
        public TileData() {
        }

        /// <summary>
        /// Gets or sets the type of variation for the given tile type.
        /// </summary>
        /// <value>
        /// The type of variation for the given tile type.
        /// </value>
        public TileTypeVariation TypeVariation {
            get {
                return this.typeVariation;
            }

            set {
                this.typeVariation = value;
                try {
                    this.tileDescription = this.TypeOfTile.ToString().ToUpperInvariant() + "_" +
                                 this.TypeVariation.ToString().ToUpperInvariant();
                } catch (ArgumentException) {
                    this.tileDescription = "unseen";
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not the tile is transparent (Does not block Field Of View).
        /// </summary>
        /// <value>
        /// A value indicating whether or not the tile is transparent.
        /// </value>
        public bool Transparent {
            get {
                return this.transparent;
            }

            set {
                this.tileChanged = true;
                this.transparent = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the tile can be seen by the player.
        /// </summary>
        /// <value>
        /// A value indicating whether the tile can be seen by the player.
        /// </value>
        public bool CanSee {
            get {
                return this.canSee;
            }

            set {
                if (this.canSee != value) {
                    if (this.seenLast && value) {
                        this.tileChanged = false;
                    } else if (!this.seenLast && value) {
                        this.seenLast = true;
                        this.tileChanged = true;
                    } else if (this.seenLast && !value) {
                        this.tileChanged = true;
                        this.seenLast = false;
                    } else {
                        this.seenLast = false;
                        this.tileChanged = true;
                    }
                }
                
                this.canSee = value;
            }
        }

        /// <summary>
        /// Gets the string description of the tile.
        /// </summary>
        /// <value>
        /// The string description of the tile.
        /// </value>
        public string TileDescription {
            get {
                // TODO: Define this further....
                if (this.IsDoor) {
                    return "DOOR_OPEN";
                } else {
                    return this.tileDescription;
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the state of the tile has changed.
        /// </summary>
        /// <value>
        /// A value indicating whether the state of the tile has changed.
        /// </value>
        public bool TileStateChanged {
            get {
                return this.tileStateChanged;
            }
            
            set {
                this.tileStateChanged = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the tile has been seen.
        /// </summary>
        /// <value>
        /// A value indicating whether the tile has been seen.
        /// </value>
        public bool HasSeen {
            get {
                return this.hasSeen;
            }
            
            set {
                this.tileChanged = true;
                this.hasSeen = value;
            }
        }
        
        /// <summary>
        /// Gets or sets the character (if any) on this tile.
        /// </summary>
        /// <value>
        /// The character on this tile.
        /// </value>
        public CharacterBase Character {
            get {
                return this.character;
            }
            
            set {
                this.character = value;
                this.tileChanged = true;
                this.TileStateChanged = true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the player character is at this tile.
        /// </summary>
        /// <value>
        /// A value indicating whether the player character is at this tile.
        /// </value>
        public bool IsPlayerHere {
            get {
                if (this.character != null) {
                    if (this.character is Player) {
                        return true;
                    }
                }
                
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether there is an item within this tile or not.
        /// </summary>
        /// <value>
        /// A value indicating whether there is an item within this tile or not.
        /// </value>
        public bool IsEmpty {
            // TODO: Add check for tile state, currently only tests for characters:
            get {
                if (this.character != null) {
                    return false;
                } else {
                    return true;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether this tile is a door tile.
        /// </summary>
        /// <value>
        /// A value indicating whether this tile is a door tile.
        /// </value>
        public bool IsDoor {
            get {
                return this.tileType == TileType.Door;
            }
        }

        /// <summary>
        /// Gets or sets the type of the tile using the TileType enumeration.
        /// </summary>
        /// <value>
        /// The type of the tile using the TileType enumeration.
        /// </value>
        public TileType TypeOfTile {
            get {
                return this.tileType;
            }
            
            set {
                this.tileChanged = true;
                this.tileType = value;
                
                // TODO: Add door states....
                if (this.tileType == TileType.Wall) {
                    this.transparent = false;
                } else {
                    this.transparent = true;
                }
                
                try {
                    this.tileDescription = this.TypeOfTile.ToString().ToUpperInvariant() + "_" +
                                 this.TypeVariation.ToString().ToUpperInvariant();
                } catch (ArgumentException) {
                    this.tileDescription = "unseen";
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not this tile was changed.
        /// </summary>
        /// <value>
        /// A value indicates whether or not this tile was changed.
        /// </value>
        public bool TileChanged {
            get {
                return this.tileChanged;
            }
            
            set {
                this.tileChanged = value;
            }
        }

        /// <summary>
        /// Generates a NULL (Empty) tile data object.
        /// </summary>
        /// <returns>A new TileData object set to NULL.</returns>
        public static TileData Null() {
            TileData temp = new TileData();
            temp.TypeOfTile = TileType.Null;
            temp.TypeVariation = TileTypeVariation.Stone;
            return temp;
        }

        /// <summary>
        /// Converts the class instance into a string representation.
        /// </summary>
        /// <returns>The class's string.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Lowercase is preferred.")]
        public override string ToString() {
            if (this.TypeOfTile == TileType.Null) {
                return "Unknown";
            } else if (this.IsPlayerHere) {
                return this.character.Name;
            } else if (this.hasSeen) {
                return this.typeVariation.ToString() + " " + 
                    this.tileType.ToString().Substring(0, 1) + 
                    this.tileType.ToString().ToLowerInvariant().Substring(1);
            } else {
                return "Unknown";
            }
        }
    }
}
